/*
 *  Objects.cpp
 *  vorpal
 *
 *  Created by rwebb on 25/04/08.
 *
 */

#include "Objects.h"
#include "VM.h"
#include "VmGroup.h"
#include "System.h"
#include "Compiler.h"
#include <string.h>
#include <math.h>

#define PI 3.14159265

namespace Vorpal{
	
void Number::Delete(){
	if(owner){
		Clear();
		owner->Recycle(this);
	}
	else{
		delete this;
	}
}

void Object::Serialize(std::ostream &output, VM *vm, SerialMemory *mem){
	bool free = false;
	uint seenIndex;
	if(!mem){
		mem = new SerialMemory();
		free = true;
	}
	
	char type = '?';
	if(this == vm->None().Ptr()){
		type = 'N';
	}
	else if(this == vm->FromBool(false).Ptr()){
		type = 'F';						
	}
	else if(this == vm->FromBool(true).Ptr()){
		type = 'T';		
	}
	else if(mem->Seen(this, &seenIndex)){
		type = '#';
	}
	else if(typeid(*this) == typeid(Number)){
		type = 'n';		
	}
	else if(typeid(*this) == typeid(String)){
		type = 's';
	}
	else if(typeid(*this) == typeid(Code)){
		type = 'c';
	}
	else if(typeid(*this) == typeid(Object)){
		type = 'o';
	}
	else if(typeid(*this) == typeid(Process)){
		type = 'p';
	}
	else{
		Assert(false);
	}
	//cout << "Serialize: " << type << endl;
	output.write(&type, 1);
	if(type == '#'){
		output.write((char *)&seenIndex, sizeof(seenIndex));
	}
	else{
		Store(output);
		ArchiveSlots(output, vm, *mem);
	}
	if(free){
		delete mem;
	}
}

Object *Object::Unserialize(std::istream &input, VM *vm, SerialMemory *mem){
	bool free = false;
	if(!mem){
		mem = new SerialMemory();
		free = true;
	}

	char type = '?';
	input.read(&type, 1);
	Object *result = vm->None().Ptr();
	//cout << "Unserialize: " << type << endl;
	switch(type){
		case 'N':
			result = vm->None().Ptr();
			break;
		case 'F':
			result = vm->FromBool(false).Ptr();
			break;
		case 'T':
			result = vm->FromBool(true).Ptr();
			break;
		case 'n':
			result = Number::Create(input);
			break;
		case 's':
			result = String::Create(input);
			break;
		case 'c':
			result = Code::Create(input);
			break;
		case 'o':
			result = Object::Create(input);
			break;
		case 'p':
			result = Process::Create(input);
			break;
		case '#': {
			uint seenIndex;
			input.read((char *)&seenIndex, sizeof(seenIndex));
			result = mem->Get(seenIndex);
			break;
		}
		default:
			Assert(false);
	}
	if(!mem->Seen(result)){
		result->UnArchiveSlots(input, vm, *mem);
	}
	if(free){
		delete mem;
	}
	return result;
}

bool Object::IsCall(Message msg, Handle<Code> &code, VM *vm){
	Handle<Object> slot = Get(msg, vm);
	if(typeid(*slot.Ptr()) == typeid(Code)){
		code.Attach( (Code *)slot.Ptr() );
		return true;
	}
	return false;
}

bool Object::Receive(Message msg, uint32_t numArgs, VM *vm){
	switch(msg){
		case msg_influence: {
#if trackInfluence
			vm->FromTop(0)->PrintInfluence();
#endif
			break;
		}
		case msg__add:
		case msg_append: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			vm->FromTop(1)->Set(vm->FromTop(1)->ArraySize(), vm->FromTop(0));
			vm->Drop(numArgs - 1);
			break;
		}
		case msg_map: {
			Handle<Object> selfObj(vm->FromTop(numArgs - 1));
			Handle<Code> codeObj = Translate<Code>(vm->FromTop(numArgs - 2).Ptr());
			DeclareArray(args, Handle<Object>, numArgs - 2);
			loop(i, 0, numArgs - 2){
				args[i] = vm->FromTop(numArgs - 3 - i);
			}
			vm->Drop(numArgs - 1);
			selfObj->ApplyToSlots(vm, codeObj, vm->GetSystem(), numArgs - 2, &args[0], false /*named*/);
			break;
		}
		case msg_size: {
			vm->Drop(numArgs);
			vm->PushPtr( vm->NewNumber(ArraySize()) );
			break;
		}
		case msg_apply: {
			if(numArgs < 2){ Failure(ERR_INVALID_NUM_ARGS); }
			Handle<Code> codeObj = Translate<Code>(vm->FromTop(numArgs - 2).Ptr());
			vm->RemoveFromTop(numArgs - 2);
			vm->Execute( codeObj, numArgs - 1 );
			break;
		}
		case msg_call: {
			if(numArgs != 3){ Failure(ERR_INVALID_NUM_ARGS); }
			Handle<Code> codeObj = Translate<Code>(vm->FromTop(1).Ptr());
			Handle<Object> args = vm->FromTop(0);
			vm->Drop(2);
			loop(i, 0, args->ArraySize()){
				Handle<Object> val = args->Get(i, vm);
				vm->Push(val);
			}
			vm->Execute( codeObj, args->ArraySize() + 1 );
			break;
		}
		case msg_fill: {
			if(numArgs != 5){ Failure(ERR_INVALID_NUM_ARGS); }
			Handle<Object> top = vm->FromTop(0);
			Handle<Number> deltaObj = Translate<Number>(vm->FromTop(1).Ptr());
			Handle<Number> limitObj = Translate<Number>(vm->FromTop(2).Ptr());
			Handle<Number> firstObj = Translate<Number>(vm->FromTop(3).Ptr());
			
			int delta = (int) deltaObj->Value();
			uint32_t limit = (uint32_t) limitObj->Value();
			uint32_t first = (uint32_t) firstObj->Value();
			if(delta == 0 || (limit > first && delta < 0)){ Failure(ERR_INVALID_DELTA_FILL); }

			for(uint32_t i = first; i < limit; i += delta){
				Set(i, top);
			}
			vm->Drop(numArgs - 1);
			break;
		}
		case msg_print: {
			cout << ToString() << endl;
			break;
		}
		case msg_put: {
			cout << ToString();
			break;
		}
		case msg__and: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Object> top = vm->FromTop(0);
			vm->DropThenReplaceTop(1, vm->FromBool( vm->ToBool(top.Ptr()) && vm->ToBool(this) ) );
			break;
		}
		case msg__or: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Object> top = vm->FromTop(0);
			vm->DropThenReplaceTop(1, vm->FromBool( vm->ToBool(top.Ptr()) || vm->ToBool(this) ) );
			break;
		}
		case msg__not: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->Drop(1);
			vm->PushBool(!vm->ToBool(this));
			break;
		}
		case msg__equal: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			vm->DropThenReplaceTop(1, vm->FromBool( vm->FromTop(0).Ptr() == this ) );
			break;
		}
		case msg__not_equal: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			vm->DropThenReplaceTop(1, vm->FromBool( vm->FromTop(0).Ptr() != this ) );
			break;
		}
		case msg_publish: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->PublishState(vm->FromTop(0));
			break;
		}
		case msg_spawn: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			Handle<Object> top(vm->FromTop(0));
			vm->Drop(numArgs);
			string processName = TheGroup().ReserveName();
			RemoteCall rc(MSG_spawn, processName);
			rc.AddArg(top, vm);
			vm->Outgoing(rc);
			vm->PushPtr( new Process( processName.c_str() ) );
			break;
		}
		case msg_process: {
			vm->Drop(numArgs);
			vm->PushPtr(new Process( TheGroup()[vm].c_str() ));
			break;
		}
		case msg_load: {
			if(numArgs < 2){ Failure(ERR_INVALID_NUM_ARGS); }
			Handle<String> top = Translate<String>( vm->FromTop(numArgs - 2).Ptr() );
			Handle<Code> code;
			if(!vm->LoadCode(code, top->ToString().c_str())){
				Failure(ERR_FILE_LOAD);
			}
			
			vm->CopyFromTop(numArgs - 2, numArgs - 1); // copy self over string object
			vm->Execute(code, numArgs - 1);
			vm->CopyFromTop(1, 0); // copy return object to result 
			vm->Drop(1); // arguments removed by the code that was executed
			break;
		}
		default: {
			if(vm->StartSystemCall(msg_object, msg, numArgs)){
				return true;
			}
			// TODO: might not be the best place to hook in universal functions
			return TheGroup().Receive(msg, numArgs, vm);
		}
	}
	return true;
}

bool Number::Receive(Message msg, uint32_t numArgs, VM *vm){
// TODO: need better error handling than calling Failure 
	switch(msg){
		case msg__sub: {
			if(numArgs == 1){
				vm->FromTop(0) = vm->NewNumber(-value);
			}
			else if(numArgs == 2){
				Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
				double result = value - top->value;
				vm->Drop(2);
				vm->PushPtr(vm->NewNumber(result));
				break;
			}
			else{
				Failure(ERR_INVALID_NUM_ARGS);
			}
			break;
		}
		case msg__div: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			vm->DropThenReplaceTop(1, vm->NewNumber(value / top->value));
			break;
		}
		case msg__mul: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			vm->DropThenReplaceTop(1, vm->NewNumber(value * top->value));
			break;
		}
		case msg__add: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			vm->DropThenReplaceTop(1, vm->NewNumber(value + top->value));
			break;
		}
		case msg__gteq: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			vm->DropThenReplaceTop(1, vm->FromBool(value >= top->value));
			break;
		}
		case msg__lteq: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			vm->DropThenReplaceTop(1, vm->FromBool(value <= top->value));
			break;
		}
		case msg__lt: {			
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			vm->DropThenReplaceTop(1, vm->FromBool(value < top->value));
			break;
		}
		case msg__gt: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			vm->DropThenReplaceTop(1, vm->FromBool(value > top->value));
			break;
		}
		case msg__not_equal: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Object> top(vm->FromTop(0).Ptr());
			bool result = (typeid(*(top.Ptr())) != typeid(Number)) || (value != ((Number *)(top.Ptr()))->value);
			vm->DropThenReplaceTop(1, vm->FromBool(result));
			break;
		}
		case msg__equal: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Object> top(vm->FromTop(0).Ptr());
			bool result = (typeid(*(top.Ptr())) == typeid(Number)) && (value == ((Number *)(top.Ptr()))->value);
			vm->DropThenReplaceTop(1, vm->FromBool(result));
			break;
		}
		case msg__rem: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			vm->DropThenReplaceTop(1, vm->NewNumber( fmod(value, top->value) ));
			break;
		}
		case msg__pow: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Number> top = Translate<Number>(vm->FromTop(0).Ptr());
			double result = pow(value, top->value);
			vm->Drop(2);
			vm->PushPtr(vm->NewNumber(result));
			break;
		}
		case msg_abs: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			double result = fabs(value);
			vm->FromTop(0) = vm->NewNumber(result);
			break;
		}
		case msg_floor: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(floor(value));
			break;
		}
		case msg_ceil: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(ceil(value));
			break;
		}
		case msg_round: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			//double result = round(value); //This is not C++xx standard (yet), it is C99
			double result = value >= 0 ? floor(value + 0.5) : ceil(value - 0.5);
			vm->FromTop(0) = vm->NewNumber(result);
			break;
		}
		case msg_exp: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(exp(value));
			break;
		}
		case msg_log: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(log(value));
			break;
		}
		case msg_sqrt: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(sqrt(value));
			break;
		}
		case msg_log10: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(log10(value));
			break;
		}
		case msg_sin: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(sin(value * PI / 180.0));
			break;
		}
		case msg_cos: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(cos(value * PI / 180.0));
			break;
		}
		case msg_tan: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(tan(value * PI / 180.0));
			break;
		}
		case msg_invsin: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(asin(value) * 180.0 / PI);
			break;
		}
		case msg_invcos: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(acos(value) * 180.0 / PI);
			break;
		}
		case msg_invtan: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(atan(value) * 180.0 / PI);
			break;
		}
		case msg_tanh: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(tanh(value));
			break;
		}
		default: {
			return Object::Receive(msg, numArgs, vm) || vm->StartSystemCall(msg_number, msg, numArgs);
		}
	}
	return true;
}

	
bool Process::Receive(Message msg, uint32_t numArgs, VM *vm){
	// FIX: need better error handling than calling Failure 
	switch(msg){
		case msg_uniqueId: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(GlobalUniqueId());
			break;
		}
		case msg__arrow: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Process> top = Translate<Process>(vm->FromTop(0).Ptr());
			Handle<Process> next = Translate<Process>(vm->FromTop(1).Ptr());
			vm->AttachOutgoing(next->Id(), top->Id(), false);
			vm->Drop(1);
			break;
		}
		case msg__or: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Process> top = Translate<Process>(vm->FromTop(0).Ptr());
			Handle<Process> next = Translate<Process>(vm->FromTop(1).Ptr());
			vm->AttachOutgoing(next->Id(), top->Id(), true);
			vm->Drop(1);
			break;
		}
		case msg_state: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = TheGroup()[processId]->GetState(vm);
			break;
		}
		case msg_kill: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			RemoteCall rc(MSG_kill, processId);
			vm->Outgoing(rc);
			vm->FromTop(0) = vm->None();
			break;
		}
		case msg_later: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			if(processId != TheGroup()[vm]){
				Failure(ERR_EVENT_NOT_ALLOWED);
			}
			vm->DeferCurrentEvent();
		}
		case msg_redirect: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			if(processId != TheGroup()[vm]){
				Failure(ERR_EVENT_NOT_ALLOWED);
			}
			Handle<Process> top = Translate<Process>(vm->FromTop(0).Ptr());
			vm->RedirectCurrentEvent(top->processId);
		}
		default: {
			if(Object::Receive(msg, numArgs, vm)  || vm->StartSystemCall(msg_process, msg, numArgs)){
				return true;
			}
			// send RemoteCall
			RemoteCall rc(msg, processId);
			for(int i = numArgs - 2; i >= 0; --i){
				rc.AddArg(vm->FromTop(i), vm);
			}
			//TheGroup().Show();
			//cout << "\nrpc: " << processId << " " << TheGroup()[processId] << endl;
			vm->Outgoing(rc);
			vm->Drop(numArgs - 1);
		}
	}
	return true;
}

Handle<Object> String::Find(string substring, uint32_t start, VM *vm) const{
	// Not calling 3 param version of Find for this one as it creates 
	// a new substring which is innefficient.			
	size_t i = str.find(substring, start);
	if(i != string::npos){
		return Handle<Object>(vm->NewNumber((double) i));
	}
	else{
		return vm->FromBool(false);
	}
}

Handle<Object> String::Find(string substring, uint32_t start, uint32_t end, VM *vm) const{
	string s = str.substr(start, end);
	size_t i = s.find(substring);
	if (i != string::npos){
		return Handle<Object>(vm->NewNumber((double) i));
	}
	else{
		return vm->FromBool(false);
	}
}
	
bool String::Receive(Message msg, uint32_t numArgs, VM *vm){
// FIX: need better error handling than calling Failure 
	switch(msg){
		case msg__add: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Object> result(new String(str + vm->FromTop(0)->ToString()));
			vm->DropThenReplaceTop(1, result);
			break;
		}
		case msg_input: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = new String(InputString());
			break;
		}
		case msg_length: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber(Length());
			break;
		}
		case msg_number: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0) = vm->NewNumber( atof(ToString().c_str()) );
			break;
		}
		case msg_strcmp: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			double result = StringCompare(vm->FromTop(0)->ToString());
			vm->Drop(numArgs);
			vm->PushPtr(vm->NewNumber(result));
			break;
		}
		case msg__equal: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			bool result = str == vm->FromTop(0)->ToString();
			vm->DropThenReplaceTop(1, vm->FromBool(result));
			break;
		}
		case msg__not_equal: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			bool result = str != vm->FromTop(0)->ToString();
			vm->DropThenReplaceTop(1, vm->FromBool(result));
			break;
		}
		case msg_substring: {
			String *result;	
			if(numArgs == 1){
				result = Substring();	
			}
			else if(numArgs == 2){
				uint32_t startIndex = uint32_t(Translate<Number>(vm->FromTop(0).Ptr())->Value());
				result = Substring(startIndex);
			}
			else if(numArgs == 3){
				uint32_t startIndex = uint32_t(Translate<Number>(vm->FromTop(1).Ptr())->Value());
				uint32_t endIndex   = uint32_t(Translate<Number>(vm->FromTop(0).Ptr())->Value());
				result = Substring(startIndex, endIndex);
			}
			else{ 
				Failure(ERR_INVALID_NUM_ARGS); result = 0; 
			}
			vm->DropThenReplaceTop(numArgs - 1, result);
			break;
		}
		case msg_left: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			String *result = Substring(0, uint32_t(Translate<Number>(vm->FromTop(0).Ptr())->Value()));
			vm->DropThenReplaceTop(1, result);
			break;
		}
		case msg_find: {
			if(numArgs < 2){ Failure(ERR_INVALID_NUM_ARGS); }
			if(numArgs == 2){
				Handle<Object> result = Find(vm->FromTop(0)->ToString(), vm);
				vm->DropThenReplaceTop(numArgs - 1, result);
			}
			else if(numArgs == 3){
				uint32_t startIndex = uint32_t(Translate<Number>(vm->FromTop(0).Ptr())->Value());
				Handle<Object> result = Find(vm->FromTop(1)->ToString(), startIndex, vm);
				vm->DropThenReplaceTop(numArgs - 1, result);
			}
			else if(numArgs == 4){
				uint32_t startIndex = uint32_t(Translate<Number>(vm->FromTop(1).Ptr())->Value());
				uint32_t endIndex = uint32_t(Translate<Number>(vm->FromTop(0).Ptr())->Value());
				Handle<Object> result = Find(vm->FromTop(2)->ToString(), startIndex, endIndex, vm);
				vm->DropThenReplaceTop(numArgs - 1, result);
			}
			else{ 
				Failure(ERR_INVALID_NUM_ARGS); 
			}
			break;
		}
		case msg_open: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Handle<Object> result = new FileIO(str, vm->FromTop(0)->ToString());
			vm->DropThenReplaceTop(numArgs - 1, result);
			break;
		}
		default: {
			return Object::Receive(msg, numArgs, vm) || vm->StartSystemCall(msg_string, msg, numArgs);
		}
	}
	return true;
}

string FileIO::Builtins_Str() const
{
	return ("read, write, close");
}

//methods for the FileIO object
bool FileIO::Receive(Message msg, uint32_t numArgs, VM *vm) {
	// FIX: need better error handling than calling Failure
	switch(msg){
			/*  Please put any new messages you list here into the string in
			 Builtins_Str() above. */
		case msg_read: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			String *result = new String( Read() );
			vm->DropThenReplaceTop(numArgs - 1, result);
			break;
		}
		case msg_store: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			vm->FromTop(0)->Serialize(file, vm);
			vm->Drop(numArgs - 1);
			break;
		}
		case msg_recall: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			Object *result = Object::Unserialize(file, vm);
			vm->DropThenReplaceTop(numArgs - 1, result);
			break;
		}
		case msg_write: {
			RuntimeAssert(numArgs == 2, ERR_INVALID_NUM_ARGS);
			Write(vm->FromTop(0)->ToString());
			vm->Drop(numArgs - 1);
			break;
		}
		case msg_close: {
			RuntimeAssert(numArgs == 1, ERR_INVALID_NUM_ARGS);
			bool result = Close();
			vm->DropThenReplaceTop(numArgs - 1, vm->FromBool(result));
			break;
		}
		default: {
			return Object::Receive(msg, numArgs, vm) || vm->StartSystemCall(msg_file, msg, numArgs);
		}
	}
	return true;
}

string FileIO::Read() {
	//read from file - if it is open in read mode
	string buffer;
	if( file.is_open() && openMode == 'r') {
		getline(file, buffer);
		if (file.eof()) buffer = "EOF";
	}
	else {
		Failure(ERR_FILE_READ);
	}
	return buffer;
}

string FileIO::Write(string source) {
	//write to file - if it is open in write mode
	if( file.is_open() && openMode == 'w' ) {
		file<< source << '\n';
	}
	else {
		Failure(ERR_FILE_WRITE);
	}
	return source;
}

bool FileIO::Close() {
	//close the file - if it is open
	if( file.is_open() ) {
		file.close();
		openMode = 'c';
		filePath = "";
		return true;
	}
	return false;
}

void FileIO::OpenFile( string path, string mode) {
	using namespace std;
	if (openMode == 'c') {
		filePath = path;
		if ( mode.compare("read") == 0 ) {
			openMode = 'r';
			file.open( path.c_str(), ios_base::in );
		}
		else if ( mode.compare("write") == 0 ) {
			openMode = 'w';
			file.open( path.c_str(), (ios_base::out | ios_base::trunc) );
		}
		else {
			Failure(ERR_FILE_INVALID_MODE);
		}
	}
	if( !file.is_open() ) {
		Failure(ERR_FILE_OPEN_FAILED);
	}
}
	
}

